# Python 趣味编程：第 3 章
#    练习问题 3.1
# 用推杆打一次球的游戏
# --------------------------
# 程序名: ex03-1-paddle.py

from tkinter import *
from dataclasses import dataclass
import random
import time

# 初始状态的设定
SPEEDS = [-3, -2, -1, 1, 2, 3]  # 球的x方向初速选项
DURATION = 0.01                 # 绘图间隔(秒)
BALL_X0 = 400                   # 球的初始位置(x)
BALL_Y0 = 100                   # 球的初始位置(y)
PADDLE_X0 = 350                 # 推杆的初始位置(×)
PADDLE_Y0 = 500                 # 推杆的初始位置(y)
PADDLE_VX = 5                   # 推杆的速度

BALL_VX = random.choice(SPEEDS) # 球的x方向初速
BALL_VY = 3                     # 球的y方向初速

# 准备改变的颜色
COLORS = ["blue", "red", "green", "yellow", "brown", "gray"]

# -------------------------
@dataclass
class Ball:
    id: int
    x: int
    y: int
    vx: int
    vy: int
    d: int
    c: str

@dataclass
class Paddle:
    id: int
    x: int
    y: int
    w: int
    h: int
    vx: int
    c: str
# -------------------------
# ball
# 球的绘制和定义
def make_ball(x, y, vx, vy, d=3, c="black"):
    id = canvas.create_oval(x, y, x + d, y + d,
                            fill=c, outline=c)
    return Ball(id, x, y, vx, vy, d, c)

# 球的移动
def move_ball(ball):
    ball.x += ball.vx
    ball.y += ball.vy

# 球的重新绘制
def redraw_ball(ball):
    canvas.coords(ball.id, ball.x, ball.y,
                  ball.x + ball.d, ball.y + ball.d)

# -------------------------
# paddle
# 推杆的绘制、定义
def make_paddle(x, y, w=100, h=20, c="blue"):
    id = canvas.create_rectangle(x, y, x + w, y + h,
                                 fill=c, outline=c)
    return Paddle(id, x, y, w, h, 0, c)

# 推杆的移动(左右)
def move_paddle(pad):
    pad.x += pad.vx

# 改变车轴的颜色
def change_paddle_color(pad, c="red"):
    canvas.itemconfigure(pad.id, fill=c)
    canvas.itemconfigure(pad.id, outline=c)
    redraw_paddle(pad)

# 推杆的重绘
def redraw_paddle(pad):
    canvas.coords(pad.id, pad.x, pad.y,
                  pad.x + pad.w, pad.y + pad.h)

# -------------------------
# 推杆操作的事件处理
def left_paddle(event):        # 将速度设定为向左(负)
    paddle.vx = -PADDLE_VX

def right_paddle(event):       # 将速度设定为向右(正)
    paddle.vx = PADDLE_VX

def stop_paddle(event):        # 将速度设定为零
    paddle.vx = 0

# =================================================
tk = Tk()
tk.title("Game")

canvas = Canvas(tk, width=800, height=600, bd=0, highlightthickness=0)
canvas.pack()
tk.update()

# ------------------
# 准备绘图道具
paddle = make_paddle(PADDLE_X0, PADDLE_Y0)
ball = make_ball(BALL_X0, BALL_Y0, BALL_VX, BALL_VY, 10)

# 连接事件和事件处理程序
canvas.bind_all('<KeyPress-Left>', left_paddle)
canvas.bind_all('<KeyPress-Right>', right_paddle)
canvas.bind_all('<KeyRelease-Left>', stop_paddle)
canvas.bind_all('<KeyRelease-Right>', stop_paddle)

# -------------------------
# 程序的主循环
while True:
    move_paddle(paddle)       # 车轴的移动
    move_ball(ball)           # 球的移动
    # 只操作一次的结束条件
    if ball.y + ball.vy <= 0: break
    if ball.x + ball.d + ball.vx >= 800 : break
    if ball.x <= 0: break
    if ball.y + ball.d + ball.vy >= 600 : break

    # 球的下方触到推杆的上面，横向位置与推杆重叠
    if (paddle.y <= ball.y + ball.d <= paddle.y + paddle.h \
        and paddle.x <= ball.x + ball.d/2 <= paddle.x + paddle.w):
        change_paddle_color(paddle, random.choice(COLORS)) # 改变颜色
        ball.vy = -ball.vy    # 球的移动方向改变

    redraw_paddle(paddle)     # 推杆的重绘
    redraw_ball(ball)         # 球的重新绘制
    tk.update()               # 绘图反映在画面上
    time.sleep(DURATION)      # 在接下来的绘图之前，都要sleep
